#include "Avalon/SignalHandling/QueuedSignalHandler.hpp"
#include "Avalon/Base/Algorithm.hpp"

using namespace Avalon;
using namespace Avalon::SignalHandling;
using namespace boost;
using namespace boost::signals2;
using namespace std;

QueuedSignalHandler::Callback::~Callback() {}

QueuedSignalHandler::CallbackZero::CallbackZero(
    const signals2::signal<void ()>::slot_type& slot)
    : m_slot(slot) {}

QueuedSignalHandler::CallbackZero::~CallbackZero() {}

void QueuedSignalHandler::CallbackZero::Invoke() {
  m_slot();
}

QueuedSignalHandlerPtr QueuedSignalHandler::Create() {
  return QueuedSignalHandlerPtr(new QueuedSignalHandler());
}

QueuedSignalHandler::~QueuedSignalHandler() {
  Clear(m_slots);
}

void QueuedSignalHandler::HandleSignals() {
  vector<Callback*> slots;
  {
    boost::lock_guard<boost::mutex> lock(m_mutex);
    std::swap(slots, m_slots);
  }
  for(vector<Callback*>::const_iterator i = slots.begin();
      i != slots.end(); ++i) {
    try {
      (*i)->Invoke();
    } catch(...) {}
    delete *i;
  }
}

connection QueuedSignalHandler::ConnectQueuedSignal(
    const QueuedSignal::slot_type& slot, bool* isQueued) {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  if(isQueued != NULL) {
    *isQueued = !m_slots.empty();
  }
  return m_queuedSignal.connect(slot);
}

QueuedSignalHandler::QueuedSignalHandler() {}

void QueuedSignalHandler::Slot(
    const signals2::signal<void ()>::slot_type& slot) {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  m_slots.push_back(new CallbackZero(slot));
  if(m_slots.size() == 1) {
    m_queuedSignal();
  }
}
